using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Pinwheel.Toolkits;

namespace Pinwheel.LSerialize
{
    public static unsafe partial class LSerializeExtension
    {
        internal static class Cache 
        {
            [ThreadStatic]
            private static bool s_Init;
            [ThreadStatic]
            private static Dictionary<int, long> s_DictRefId;
            
            static Cache()
            {
                CheckInitCache();
            }
            
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            static void CheckInitCache()
            {
                if (s_Init) return;
                // s_Lookup = new Dictionary<int, List<int>>();
                s_DictRefId = new Dictionary<int, long>();
                s_Init = true;
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            internal static bool CacheReference(ref int hash, ref long refId)
            {
                CheckInitCache();
                bool result = s_DictRefId.TryAdd(hash, refId);
                return result;
            }
            
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            internal static bool TryGetReference(ref int hash, out long refId)
            {
                CheckInitCache();
                return s_DictRefId.TryGetValue(hash, out refId);
            }
        
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            internal static bool RemoveReference(ref int hash)
            {
                CheckInitCache();
                return s_DictRefId.Remove(hash, out _);
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            internal static void RemoveAllReference()
            {
                Profiler.Start("LSerializeExtension.Cache.Remove");
                CheckInitCache();
                s_DictRefId.Clear();
                Profiler.End();
            }
        
            // internal static void RemoveAllReference<T>(T* buffer) where T: unmanaged, ILStreamBuffer
            // {
            //     Profiler.Start("LSerializeExtension.Cache.Remove");
            //     var buffHash = buffer->GetHashCode();
            //     if (!s_Lookup.TryGetValue(buffHash, out var list)) return;
            //     for (int i = 0; i < list.Count; i++)
            //         RemoveReference(list[i]);
            //     s_Lookup.Remove(buffHash, out _);
            //     Profiler.End();
            // }
        }
        
        internal static class Cache<T>
        {
            [ThreadStatic] private static T[] s_Cache;
            
            static T[] CacheArray
            {
                get
                {
                    if (null! == s_Cache)
                    {
                        s_Cache = new T[16];
                    }
                    return s_Cache;
                }
            }

            internal static void CreateCache(long refId, ref T obj)
            {
                if (null == obj) return;
                Profiler.Start("LSerializeExtension.Cache<T>.Create");
                if (refId >= CacheArray.Length)
                {
                    var newSize = CacheArray.Length << 1;
                    for (int i = 0; i < 64; i++)
                    {
                        if (refId < newSize) break;
                        newSize <<= 1; 
                    }
                    Array.Resize(ref s_Cache, newSize);
                }
                CacheArray[refId] = obj;
                Profiler.End();
            }
            
            internal static bool TryGetCache(long refId, out T obj)
            {
                if (refId < 0 || refId > CacheArray.Length)
                {
                    obj = default(T);
                    return false;
                }
                obj = CacheArray[refId];
                return null != obj;
            }
            
            internal static void RemoveAllCache()
            {
                Array.Clear(CacheArray, 0, CacheArray.Length);
            }
        }
        
        internal static class CacheObj
        {
            // [ThreadStatic]
            // private static Dictionary<int, List<long>> s_Lookup;
            // [ThreadStatic]
            // private static Dictionary<long, object> s_DictObj;

            [ThreadStatic] private static bool s_Init;
            [ThreadStatic] private static object[] s_Lookup;
            [ThreadStatic] private static int s_Index;

            static CacheObj()
            {
                // s_Lookup = new Dictionary<int, List<long>>();
                // s_DictObj = new Dictionary<long, object>();
                s_Index = 0;
                s_Lookup = new object[16];
            }
            
            static object[] Lookup
            {
                get
                {
                    if (null! == s_Lookup)
                    {
                        s_Lookup = new object[16];
                    }
                    return s_Lookup;
                }
            }            
            
            internal static void CacheSerializeObj<O>(long refId, ref O obj)
            {
                if (null == obj) return;
                Profiler.Start("LSerializeExtension.CacheObj.Create");
                s_Index = (refId > s_Index) ? (int) refId : s_Index;
                if (s_Index >= Lookup.Length)
                    Array.Resize(ref s_Lookup, s_Index << 1);
                Lookup[refId] = obj;
                Profiler.End();
            }
            
            internal static bool TryGetSerializeObj<O>(long refId, out O obj)
            {
                if (refId < 0 || refId > s_Index)
                {
                    obj = default(O);
                    return false;
                }
                obj = (O) Lookup[refId];
                return null != obj;
            }

            internal static void RemoveAllSerializeObj()
            {
                s_Index = 0;
                Array.Clear(Lookup, 0, Lookup.Length);
            }
        }
    }
}
